package com.weixing.mall.core.converter;

import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.weixing.mall.base.enums.IBaseEnum;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.readers.parameter.Examples;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.io.Serializable;
import java.util.List;

@Component
public class MySwaggerExpandedParameterBuilder implements ExpandedParameterBuilderPlugin {
    private final DescriptionResolver descriptions;
    private final EnumTypeDeterminer enumTypeDeterminer;
    private String desp;

    @Autowired
    public MySwaggerExpandedParameterBuilder(DescriptionResolver descriptions, EnumTypeDeterminer enumTypeDeterminer) {
        this.descriptions = descriptions;
        this.enumTypeDeterminer = enumTypeDeterminer;
    }

    @Override
    public void apply(ParameterExpansionContext context) {
        this.desp = "";
        Optional<ApiModelProperty> apiModelPropertyOptional = context.findAnnotation(ApiModelProperty.class);
        if (apiModelPropertyOptional.isPresent()) {
            this.fromApiModelProperty(context, (ApiModelProperty) apiModelPropertyOptional.get());
        }

        Optional<ApiParam> apiParamOptional = context.findAnnotation(ApiParam.class);
        if (apiParamOptional.isPresent()) {
            this.fromApiParam(context, (ApiParam) apiParamOptional.get());
        }

    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }

    private void fromApiParam(ParameterExpansionContext context, ApiParam apiParam) {
        String allowableProperty = Strings.emptyToNull(apiParam.allowableValues());
        AllowableValues allowable = this.allowableValues(Optional.fromNullable(allowableProperty), context.getFieldType().getErasedType());
        this.maybeSetParameterName(context, apiParam.name())
                .description(this.descriptions.resolve(apiParam.value()))
                .defaultValue(apiParam.defaultValue())
                .required(apiParam.required())
                .allowMultiple(apiParam.allowMultiple())
                .allowableValues(allowable)
                .parameterAccess(apiParam.access())
                .hidden(apiParam.hidden())
                .scalarExample(apiParam.example())
                .complexExamples(Examples.examples(apiParam.examples()))
                .order(-2147482648).build();
    }

    private void fromApiModelProperty(ParameterExpansionContext context, ApiModelProperty apiModelProperty) {
        String allowableProperty = Strings.emptyToNull(apiModelProperty.allowableValues());
        AllowableValues allowable = this.allowableValues(Optional.fromNullable(allowableProperty), context.getFieldType().getErasedType());
        this.maybeSetParameterName(context, apiModelProperty.name())
                .description(this.descriptions.resolve(apiModelProperty.value()) + " " + this.desp)
                .required(apiModelProperty.required())
                .allowableValues(allowable)
                .parameterAccess(apiModelProperty.access())
                .hidden(apiModelProperty.hidden())
                .scalarExample(apiModelProperty.example())
                .order(-2147482648).build();
    }

    private ParameterBuilder maybeSetParameterName(ParameterExpansionContext context, String parameterName) {
        if (!Strings.isNullOrEmpty(parameterName)) {
            context.getParameterBuilder().name(parameterName);
        }

        return context.getParameterBuilder();
    }

    private AllowableValues allowableValues(Optional<String> optionalAllowable, Class<?> fieldType) {
        AllowableValues allowable = null;
        if (this.enumTypeDeterminer.isEnum(fieldType)) {
            allowable = new AllowableListValues(this.getEnumValues(fieldType), "LIST");
        } else if (optionalAllowable.isPresent()) {
            allowable = ApiModelProperties.allowableValueFromString((String) optionalAllowable.get());
        }

        return (AllowableValues) allowable;
    }

    private List<String> getEnumValues(Class<?> subject) {
        IBaseEnum[] values = (IBaseEnum[]) subject.getEnumConstants();
        final List<String> displayValues = Lists.newArrayList();
        StringBuilder sb = new StringBuilder();
        for (IBaseEnum baseEnum : values) {
            Serializable value = baseEnum.getValue();
            String desp = baseEnum.getDesp();
            displayValues.add(String.valueOf(value));
            sb.append(value).append(":").append(desp);
        }

        this.desp = sb.toString();
        return displayValues;

    }
}